home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / scsh-0.4 / scsh-0 / scsh-0.4.2 / scsh / fdports1.c < prev    next >
C/C++ Source or Header  |  1995-10-29  |  12KB  |  459 lines

  1. /* This file contains the code for doing the scsh file ports stuff.
  2. ** Copyright (c) 1993, 1994 by Olin Shivers.
  3. **
  4. ** Note that this code mutates Scheme records -- it has the layout
  5. ** of fdports and their data records wired in. This is somewhat fragile.
  6. */
  7.  
  8. /* A note on the clearerr() calls herein: SunOS stdio input routines, 
  9. ** contrary to POSIX, will return EOF if the stream's EOF flag is set,
  10. ** without trying to read the stream. This is a lose for tty's, which
  11. ** can frequently still be read from after the first EOF (e.g., if you
  12. ** type a ^D to bag out of a breakpoint, you would like the terminal
  13. ** input port to not shut down forever.)
  14. **
  15. ** To fix this lossage, we are careful to call clearerr() before every
  16. ** input stream op.
  17. */
  18.  
  19. /* We maintain the following invariant: every open port has a FILE*
  20. ** associated with it.
  21. */
  22. #include "sysdep.h"
  23. #include <stdio.h>
  24. #include <fcntl.h>
  25. #include <unistd.h>
  26. #include <stdlib.h>
  27. #include <errno.h>
  28. #include "cstuff.h"
  29. #define NUM_FDPORTS 256
  30. #include "fdports.h"
  31. #include "machine/stdio_dep.h"
  32.  
  33. /* Make sure our exports match up w/the implementation: */
  34. #include "fdports1.h"
  35.  
  36. extern int errno;
  37.  
  38. /* Maps fd's to FILE*'s. */
  39. static FILE *fstar_cache[NUM_FDPORTS] = {NULL};
  40.  
  41. /* Maps fd's to ports. */
  42. static scheme_value fdports[NUM_FDPORTS] = {SCHFALSE};
  43.  
  44. void init_fdports(void)
  45. {
  46.     int i = NUM_FDPORTS;
  47.     while( i-- ) fdports[i] = SCHFALSE;
  48.  
  49.     /* Specially hack stdio. */
  50.     fstar_cache[fileno(stdin)]  = stdin;
  51.     fstar_cache[fileno(stdout)] = stdout;
  52.     fstar_cache[fileno(stderr)] = stderr;
  53.     }
  54.  
  55. /* (maybe_fdes2port fd)
  56. ** Return: the port if there is one allocated; otherwise #f.
  57. ** If a port is returned, the revealed count is NOT incremented.
  58. */
  59. scheme_value maybe_fdes2port(int fd)
  60. {
  61.     if( fd < 0 || fd >= NUM_FDPORTS )
  62.     return SCHFALSE;
  63.     
  64.     return fdports[fd];
  65.     }
  66.  
  67. #if 0
  68. /* Bogus old code. We now compute the mode string from the actual fd. */
  69. static char const *mode2string(int mode)
  70. {
  71.     if( mode == 0 ) return "r";
  72.     else if( mode == 1 ) return "w";
  73.     else if( mode == 2 ) return "r+";
  74.     else return "x"; /* What??? */
  75.     }
  76. #endif
  77.  
  78. static char const *fdes_modestr(int fd)
  79. {
  80.     int flags = fcntl(fd,F_GETFL);
  81.  
  82.     if( flags == -1 ) return NULL;
  83.     flags &= O_ACCMODE;
  84.  
  85.     if( flags == O_WRONLY ) return "w";
  86.     else if( flags == O_RDONLY ) return "r";
  87.     else if( flags == O_RDWR ) return "r+";
  88.  
  89.     fputs("That's impossible.\n", stderr);
  90.     abort();
  91.     _exit(-1);
  92.     /*NOTREACHED*/
  93.     }
  94.  
  95. /* Returns a char, #f for EOF, or errno. */
  96. scheme_value fdport_getchar(scheme_value data)
  97. {
  98.     int fd = EXTRACT_FIXNUM(*PortData_Fd(data));
  99.     FILE *f = fstar_cache[fd];
  100.     int c;
  101.     
  102.     clearerr(f);
  103.     c = getc(f);
  104.  
  105.     if( EOF == c )
  106.     return ferror(f) ? ENTER_FIXNUM(errno) : SCHFALSE;
  107.     else
  108.     return ENTER_CHAR(c);
  109.     }
  110.  
  111. int fdport_putchar(scheme_value data, char c)
  112. {
  113.     int fd = EXTRACT_FIXNUM(*PortData_Fd(data));
  114.     FILE *f = fstar_cache[fd];
  115.     int retval = putc(c,f);
  116.     return (retval == EOF) ? errno : 0;
  117.     }
  118.  
  119.  
  120. /* Not POSIX, so we punt to an OS-specific routine. */
  121. scheme_value fdport_char_readyp(scheme_value data)
  122. {
  123.     extern scheme_value stream_char_readyp(FILE *);
  124.     return stream_char_readyp(fstar_cache[EXTRACT_FIXNUM(*PortData_Fd(data))]);
  125.     }
  126.  
  127.  
  128. int flush_fdport(scheme_value data)
  129. {
  130.     FILE *f = fstar_cache[EXTRACT_FIXNUM(*PortData_Fd(data))];
  131.     return fflush(f) ? errno : 0;
  132.     }
  133.  
  134. /* This is actually just fflush(NULL), but apparently the pinheads
  135. ** at Sun can't be bothered to implement ANSI C or POSIX, so this op
  136. ** cleverly dumps core. Hence we do this incomplete approximation.
  137. */
  138.  
  139. int flush_all_ports(void)
  140. {
  141.     int i;
  142.     for(i=0; i<NUM_FDPORTS; i++)
  143.     if(fstar_cache[i]) fflush(fstar_cache[i]);
  144.     return 0;
  145. /*  return fflush(NULL) ? errno : 0;  THE REAL SOLUTION.*/
  146.     }
  147.  
  148. int seek_fdport(scheme_value data, off_t offset, int whence, int *newpos)
  149. {
  150.     FILE *f = fstar_cache[EXTRACT_FIXNUM(*PortData_Fd(data))];
  151.     *PortData_Peek(data) = SCHFALSE; /* Flush buffered data. */
  152.  
  153.     if( fseek(f, offset, whence) )        /* seek */
  154.     { *newpos = 0; return errno; }
  155.  
  156.     *newpos = ftell(f);                /* tell */
  157.     return (*newpos < 0) ? errno : 0;
  158.     }
  159.  
  160.  
  161. int tell_fdport( scheme_value data, int *newpos )
  162. {
  163.     FILE *f = fstar_cache[EXTRACT_FIXNUM(*PortData_Fd(data))];
  164.     *newpos = ftell(f);
  165.     return (*newpos < 0) ? errno : 0;
  166.     }
  167.     
  168.  
  169. int set_fdbuf( scheme_value data, int policy, int bufsize )
  170. {
  171.     FILE *f = fstar_cache[EXTRACT_FIXNUM(*PortData_Fd(data))];
  172.     int size = (bufsize < 0) ? BUFSIZ : bufsize;
  173.     return setvbuf(f, NULL, policy, size) ? errno : 0;
  174.     }
  175.  
  176. int close_fdport(scheme_value port_data)
  177. {
  178.     if( *PortData_Closed(port_data) == SCHFALSE ) {
  179.     int fd = EXTRACT_FIXNUM(*PortData_Fd(port_data));
  180.     FILE *f = fstar_cache[fd];
  181.  
  182.     *PortData_Fd(port_data) = SCHFALSE;
  183.     fdports[fd] = SCHFALSE;
  184.     *PortData_Closed(port_data) = SCHTRUE;
  185.     *PortData_Peek(port_data) = SCHFALSE;
  186.     fstar_cache[fd] = NULL;
  187.     return fclose(f) ? errno : 0;
  188.     }
  189.     else return EBADF; /* Already closed. */
  190.     }
  191.  
  192.  
  193. static int cloexec_fdport(scheme_value port_data)
  194. {
  195.     int fd = EXTRACT_FIXNUM(*PortData_Fd(port_data));
  196.  
  197.     return fcntl(fd, F_SETFD, FD_CLOEXEC) ? errno : 0;
  198.     }
  199.  
  200.  
  201. /*  Set all the unrevealed ports to close-on-exec.
  202.     This is called right before an exec, which is sleazy;
  203.     we should have the port-revealing machinery set and reset
  204.     this value.
  205. */
  206. void cloexec_unrevealed(void)
  207. {
  208.     int i;
  209.     for(i=0; i<NUM_FDPORTS; i++) {
  210.     scheme_value port = fdports[i];
  211.     if( port != SCHFALSE ) {
  212.         scheme_value data = *Port_PortData(port);
  213.         if( *PortData_Rev(data) == 0 ) cloexec_fdport(data);
  214.         }
  215.     }
  216.     }
  217.  
  218.  
  219. int install_port(int fd, scheme_value port)
  220. {
  221.     FILE *stream;
  222.     const char *modestr;
  223.  
  224.     if( fd < 0 || fd >= NUM_FDPORTS ) return -1;
  225.     if( fdports[fd] != SCHFALSE ) return -2;
  226.     if( !(modestr = fdes_modestr(fd)) )
  227.     return -3;
  228.  
  229.     fdports[fd] = port;
  230.  
  231.     if( fstar_cache[fd] ) return 0; /* A hack mainly for stdio. */
  232.  
  233.     fstar_cache[fd] = stream = fdopen(fd, modestr);
  234.     return stream ? 0 : errno;
  235.     }
  236.  
  237.  
  238. FILE *fdes2fstar(int fd)
  239. {
  240.     if( fstar_cache[fd] ) return fstar_cache[fd];
  241.     else {
  242.     const char *modestr = fdes_modestr(fd);
  243.     return modestr ? fdopen(fd, modestr) : NULL;
  244.     }
  245.     }
  246.  
  247. /* fd_from's FILE* structure is changed to be fd_to's FILE* structure.
  248. ** So buffered data isn't lost. Return 0 on failure.
  249. ** Rather non-portable.
  250. */
  251. static int move_fstar(int fd_from, int fd_to)
  252. {
  253.     FILE *f1 = fdes2fstar(fd_from);
  254.     if( !f1 ) return 0;
  255.     setfileno(f1, fd_to);
  256.     fstar_cache[fd_from] = NULL;
  257.     fstar_cache[fd_to] = f1;
  258.     return 1;
  259.     }
  260.  
  261.  
  262. /* Move port so that it's underlying file descriptor is fd.
  263. ** The port's underlying FILE* is also shifted over, so that
  264. ** buffered data isn't lost on a shift. Return 0 on success.
  265. */
  266. int move_fdport(int fd, scheme_value port, int new_revealed)
  267. {
  268.     scheme_value port_data = *Port_PortData(port);
  269.     int old_fd = EXTRACT_FIXNUM(*PortData_Fd(port_data));
  270.  
  271.     if( fd < 0 || fd >= NUM_FDPORTS ) return 1;
  272.  
  273.     /* You are allowed to "move" a port to its current fd.
  274.        Otherwise, the fd must be unallocated. Kluge. */
  275.  
  276.     if( fdports[fd] != port ) {
  277.     if( fdports[fd] != SCHFALSE ) return 1; /* Target already allocated. */
  278.  
  279.     if( !move_fstar(old_fd, fd) ) return 1;
  280.     
  281.     fdports[fd] = port;
  282.     fdports[old_fd] = SCHFALSE;
  283.     *PortData_Fd(port_data) = ENTER_FIXNUM(fd);
  284.     }
  285.  
  286.     /* Unreveal the port by shifting the revealed count
  287.        over to the old-revealed count. */
  288.     *PortData_OldRev(port_data) = ENTER_FIXNUM(EXTRACT_FIXNUM(*PortData_OldRev(port_data))+
  289.                      EXTRACT_FIXNUM(*PortData_Rev(port_data)));
  290.     *PortData_Rev(port_data) = ENTER_FIXNUM(new_revealed);
  291.     return 0;
  292.     }
  293.  
  294. /* Scan the fdports vector after a gc. Uncopied unrevealed ports 
  295. ** have their fds closed. The fdports vec is updated with the copy.
  296. */
  297. #ifdef TEST_GC
  298. void post_gc_fdports(void) {}
  299. #else
  300. void post_gc_fdports(void)
  301. {
  302.     int fd;
  303.  
  304. #ifdef NOISY_FDGC
  305.     fputs("{GC", stderr); fflush(stderr);
  306. #endif
  307.     for(fd=0; fd<NUM_FDPORTS; fd++) {
  308.  
  309.     scheme_value port = fdports[fd];
  310.     if(STOBP(port)) {
  311.         long header = STOB_HEADER(port);
  312.         if(STOBP(header)) {
  313. #ifdef NOISY_FDGC
  314.         fprintf(stderr, "Copying port[fd] %d[%d] header %d\n",
  315.             port, fd, header);
  316.         fflush(stderr);
  317. #endif
  318.         /* Port was copied, so update fdports entry. */
  319.         fdports[fd] = header;
  320.         }
  321.     
  322.         else {
  323.         /* Port wasn't copied -- is garbage.
  324.            If fd unrevealed, close it. */
  325.         int rev = EXTRACT_FIXNUM(*PortRev(port));
  326. #ifdef NOISY_FDGC
  327.         fprintf(stderr, "GC'ing %srevealed port[fd] %d[%d]\n",
  328.             rev == 0 ? "un" : "",
  329.             port, fd);
  330.         fflush(stderr);
  331. #endif
  332.         if( rev == 0 ) close_fdport(*Port_PortData(port));
  333.         fdports[fd] = SCHFALSE; /* Drop the port. */
  334.         }
  335.         }
  336.     }
  337. #ifdef NOISY_FDGC
  338.     fputs("}", stderr); fflush(stderr);
  339. #endif
  340.     }
  341. #endif
  342.  
  343. #define MIN(a,b) (((a) < (b)) ? (a) : (b))    /* Not a function. */
  344.  
  345. int read_fdport_substring(scheme_value buf, int start, int end, scheme_value data)
  346. {
  347.     extern int read_stream_substring(scheme_value, int, int, FILE*);
  348.  
  349.     scheme_value peek = *PortData_Peek(data);
  350.     FILE *f = fstar_cache[EXTRACT_FIXNUM(*PortData_Fd(data))];
  351.  
  352.     clearerr(f); /* SunOS sux. */
  353.  
  354.     /* If there's a peek char, then we'll use it and
  355.        whatever is buffered in the FILE*. */
  356.  
  357.     if( IsChar(peek) ) {
  358.     int len = end-start;
  359.     if( len > 0 ) {
  360.         char *p = StrByte(buf,start);
  361.         *p++ = EXTRACT_CHAR(peek);
  362.         return 1 + fread(p, 1, MIN(len-1, fbufcount(f)), f);
  363.         }
  364.     else return 0;
  365.     }
  366.  
  367.     /* Otherwise, just do a read_stream_substring. */
  368.     return read_stream_substring(buf, start, end, f);
  369.     }
  370.  
  371. int write_fdport_substring(scheme_value buf, int start, int end, scheme_value data)
  372. {
  373.     extern int write_stream_substring(scheme_value, int, int, FILE*);
  374.     FILE *f = fstar_cache[EXTRACT_FIXNUM(*PortData_Fd(data))];
  375.     return write_stream_substring(buf, start, end, f);
  376.     }
  377.  
  378. /* 1st return value says why we terminated the read:
  379. ** - integer errno if error.
  380. ** - char c if string terminated by char c (which is consumed).
  381. ** - eof object if string terminated by EOF
  382. ** - #f if buf overflow.
  383. **
  384. ** 2nd return value is num chars read into BUF.
  385. **
  386. ** GOBBLE boolean says whether or not to read a terminating delimiter char
  387. **    or just leave it in the input stream.
  388. **
  389. ** N.B.:
  390. ** - This code is dependent on the representation of scsh's char-sets.
  391. **   We assume they are 256-elt strings. If this changes, we'd have to
  392. **   rewrite the code.
  393. ** - This procedure is made more complex by the fact that we may have to take
  394. **   the first char read from the *port's* peek-char slot.
  395. ** - buffer-full/terminated-char ties are broken in favor of successful
  396. **   termination.
  397. */
  398.  
  399. scheme_value read_delim(const char *delims, char *buf, int gobble,
  400.             scheme_value port, int start, int end,
  401.             int *nread)
  402. {
  403.  
  404.     scheme_value data  = *Port_PortData(port);
  405.     scheme_value peekc = *PortData_Peek(data);
  406.     int fd = EXTRACT_FIXNUM(*PortData_Fd(data));
  407.     FILE *f = fstar_cache[fd];
  408.     char *cptr   = buf+start-1, /* Location of last char deposited. */
  409.          *bufend = buf+end-1;   /* Last writeable position. */
  410.     
  411.     /* This chunk of code is necessary because we have to check
  412.     ** the port's one-char pushback slot before going to the port's
  413.     ** stdio FILE*. Yech.
  414.     */
  415.     if( IsChar(peekc) ) {
  416.     char c = EXTRACT_CHAR(peekc);
  417.     if( delims[c]  ) {        /* Is c in cset? */
  418.         if( gobble ) *PortData_Peek(data) = SCHFALSE;
  419.         *nread = 0;
  420.         return peekc;
  421.         }
  422.     else if( start >= end ) {
  423.         *nread = 0;            /* Overflow. */
  424.         return SCHFALSE;
  425.         }
  426.     else {
  427.         *++cptr = c;
  428.         *PortData_Peek(data) = SCHFALSE;
  429.         }
  430.     }
  431.  
  432.     clearerr(f);
  433.  
  434.     do {
  435.     int c = getc(f);
  436.  
  437.     if( EOF == c ) {        /* Terminal case: EOF or error. */
  438.         *nread = 1 + cptr - buf - start;
  439.         return ferror(f) ? ENTER_FIXNUM(errno) : SCHEOF;
  440.         }
  441.  
  442.     else if( delims[c] ) {        /* Terminal case: delimiter char. */
  443.         scheme_value ch = ENTER_CHAR(c);
  444.         *nread = 1 + cptr - buf - start;
  445.         if( !gobble ) *PortData_Peek(data) = ch;
  446.         return ch;
  447.         }
  448.  
  449.     else if( cptr >= bufend ) {    /* Terminal case: buffer overflow. */
  450.         *PortData_Peek(data) = ENTER_CHAR(c);    /* Put C back. */
  451.         *nread = end-start;
  452.         return SCHFALSE;
  453.         }
  454.     
  455.     else *++cptr = c;
  456.     }
  457.     while (1);
  458.     }
  459.